iT邦幫忙

2022 iThome 鐵人賽

DAY 13
0
Modern Web

JS 忍者訓練計畫系列 第 13

原型之物件導向(下) Day12

  • 分享至 

  • xImage
  •  

Prototype 使用讓喜歡物件導向風格的開發者可以更接近使用物件導向功能,但需要注意一些可能造成的幽微問題,並有可能在新版本的JS裡面,protype 客製功能以新功能被實現時,產生差異。

另,實體化問題也是需要注意的部分。new 的使用尚若忽略宣告,會導致該函式以一般函式的身份進行呼叫,而不是以建構式的身份,於是不會建立新物件。JS 新手可能會掉入試著呼叫建構式卻沒加上 new 的情況,不僅會導致會預期的錯誤,也將會出現細微難辨的效果。

這章想學到什麼?

  • HTML DOM 裡的 prototype
  • 潛藏危機處理
    • 擴充 Object,造成污染
    • 延伸 Number
    • 繼承原生物件撰寫子類別
    • 實體化相關問題
  • 撰寫相似於類別的程式(極複雜,有機會補充說明)

程式碼閱讀練習與撰寫

HTML DOM 裡的 prototype

在每個 DOM 元素的 prototype 加入功能

HTMLElement.prototype.shout = function(){
    console.log("hello");
}
ƒ (){
    console.log("hello");
}
var a = document.querySelector("a");
undefined
a.shout()
VM885:2 hello

潛藏危機處理

擴充 Object,造成污染

Object.prototype.keys = function(){
    var keys = [];
    for (var p in this)
        //keys.push(p); //會造成所有物件都綁定
        if (this.hasOwnProperty(i)) keys.push(i); //改寫增加判斷是不是實體化過的
    
    return keys;
}

var obj = {a: 1, b: 2, C; 3};

obj.keys().length == 3

延伸 Number,但字面值無法使用

Number.prototype.add = function(num){
    return this+ num;
}

var n = 5;
n.add(3);
(5).add(3);
5.add(3); //發生錯誤

繼承原生物件撰寫子類別

function MyArray() {}

MyArray.prototype = new Array();

var mine = new MyArray();
mine.push(1, 2, 3);

//模擬 Array 功能但並非真正的子類別
function MyArray() {}

MyArray.prototype.length = 0;

(function(){
    var methods = ['push', 'pop', 'shift', 'unshift', 'slice', 'splice', 'join'];
    
    for (var i = 0; i < methods.length; i++) (function(name) {
    MyArray.prototype[name] = function() {
        return Array.prototype[name].apply(this, arguments);
    }
    })(methods[i])
})();

var mine = new MyArray();
var mine = new MyArray();
mine.push(1, 2, 3);

實體化相關問題

沒有使用 new 直接對全域變數造成污染。

function User(first, last){
    this.name = first + " " + last;
}

var name = "test"

var user = User("Ichigo", "Kuroaki")

console.log(name === "test") //false

撰寫相似於類別的程式

子類別方法應用

var Person = Object.subClass({
    init: function(isDancing) {
        this.dancing = isDancing;
    },
    dance: function() {
        return this.dancing;
    }
})

var Ninja = Person.subClass({
    init: function(isDancing) {
        this._super(false);
    },
    dance: function() {
        return this._super();
    },
    sqingSword: function() {
        return true;
    }
})

var person = new Person(true);
console.log(person.dance())

var ninja = new Ninja();
console.log(ninja.sqingSword())
console.log(ninja instanceof Person)

子類別方法實作

(function(){
    var initializing = false,
        superPattern = /xyz/.test(function(){ xyz }) ? /\b_super\b/ : /.*/;
        
    Object.subClass = function(properties) {
        var _super = this.prototype;
        
        initializing = true;
        var proto = new this();
        initializing = false;
        
        for (var name in properties) {
        
            proto[name] = typeof properties[name] == "function" && typeof _super[name] == "function" && superPattern.test(properites[name]) ? (function(name, fn) {
            return function() {
                var tmp = this._super;
                
                this._super = _super[name];
                
                var ret = fn.apply(this, arguments);
                this._super = tmp;
                
                return ret;
            }
            })(name, properties[name]) :
            properties[name];
        }
        
        function Class() {
            if(!initializing && this.init)
                this.init.apply(this, arguments);
        }
        
        Class.prototype = proto;
        Class.constructor = Class;
        
        Class.subClass = arguments.calle;
        return Class;
    }
})();

Any fool can write code that a computer can understand. Good programmers write code that humans can understand. – Martin Fowler

參考資料

https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Inheritance_and_the_prototype_chain


上一篇
原型之物件導向(上) Day11
下一篇
與正規表達式吵嘴(上) Day13
系列文
JS 忍者訓練計畫30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言